using BlogDemo.Common;
using BlogDemo.Core.Domain;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace BlogDemo.Infrastructure
{
    public class BlogDbContext : IdentityDbContext<IdentityUser<Guid>, IdentityRole<Guid>, Guid>, IDbContext
    {
        //public DbSet<Post> Posts { get; set; }
        //public DbSet<Blog> Blogs { get; set; }

        //public DbSet<Tag> Tags { get; set; }

        //public DbSet<BlogImage> BlogImages { get; set; }
        //public DbSet<PostTag> PostTags { get; set; }

        public BlogDbContext(DbContextOptions<BlogDbContext> options) : base(options)
        { 

        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            //if (!optionsBuilder.IsConfigured)
            //{
            //    //重点:数据迁移或者直接New BlogDbContext时候用到的链接字符串获取方式
            //    var builder = new ConfigurationBuilder()
            //        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
            //    var configuration = builder.Build();
            //    string connectionString = configuration.GetConnectionString("SQLConnection");
            //    optionsBuilder.UseSqlite(connectionString);
            //}
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            /*
            User	    表示用户。
            Role	    表示一个角色。
            UserClaim	表示用户拥有的声明。
            UserToken	表示用户的身份验证令牌。
            UserLogin	将用户与登录名相关联。
            RoleClaim	表示向角色中所有用户授予的声明。
            UserRole	关联用户和角色的联接实体。
            实体类型关系
            每个 User 可以有多个 UserClaims。
            每个 User 可以有多个 UserLogins。
            每个 User 可以有多个 UserTokens。
            每个 Role 可以有多个关联的RoleClaims。
            每个 User 可以有多个关联的 Roles，并且每个 Role 可以与多个 Users 关联。 这是一种多对多关系，需要数据库中的联接表。 联接表由 UserRole 实体表示。
            
             */

            ////Blog -> BlogImage （一对一）
            //modelBuilder.HasOne(b => b.BlogImage)
            //    .WithOne(i => i.Blog)
            //    .HasForeignKey<BlogImage>(b => b.BlogId);


            //Blog -> Post （一对多）
            //modelBuilder.HasOne<Blog>()
            //    .WithMany()
            //    .HasForeignKey(p => p.BlogId)
            //    .OnDelete(DeleteBehavior.Cascade);

            //Post -> Tag （多对多）
            //1 无中间Entity
            // Post // public ICollection<Tag> Tags { get; set; }
            // Tag  // public ICollection<Post> Posts { get; set; }

            //modelBuilder.HasMany(p => p.Tags)
            //    .WithMany(p => p.Posts)
            //    .UsingEntity(j => j.ToTable("PostTags"));

            //2 定义了PostTag
            // Post // public virtual ICollection<PostTag> PostTags { get; set; }
            // Tag  // public virtual ICollection<PostTag> PostTags { get; set; }

            modelBuilder.Entity<PostTag>().HasKey(t => new { t.PostId, t.TagId });
            // //Post -> Tag （多对多）1 有中间表 Entity
            modelBuilder.Entity<PostTag>()
                .HasOne(pt => pt.Post)
                .WithMany(p => p.PostTags)
                .HasForeignKey(pt => pt.PostId);

            modelBuilder.Entity<PostTag>()
                .HasOne(pt => pt.Tag)
                .WithMany(t => t.PostTags)
                .HasForeignKey(pt => pt.TagId);

            //2 无中间Entity
            // Post // public virtual ICollection<Tag> Tags { get; set; }
            // Tag  // public virtual ICollection<Post> Posts { get; set; }

            //modelBuilder.UsingEntity<Dictionary<string, object>>("PostTag",
            //    j => j
            //.HasOne<Tag>()
            //.WithMany()
            //.HasForeignKey("TagId")
            //.HasConstraintName("FK_PostTag_Tags_TagId")
            //.OnDelete(DeleteBehavior.Cascade),
            //j => j
            //.HasOne<Post>()
            //.WithMany()
            //.HasForeignKey("PostId")
            //.HasConstraintName("FK_PostTag_Posts_PostId")
            //.OnDelete(DeleteBehavior.ClientCascade));

            //SeedData

            modelBuilder.Entity<Blog>().HasDataBlogSeed();
            modelBuilder.Entity<Tag>().HasDataTagSeed();
            modelBuilder.Entity<Post>().HasDataPostSeed();
            modelBuilder.Entity<PostTag>().HasDataPostTagSeed();
            //IdentityUser 默认为string
            modelBuilder.Entity<IdentityUser<Guid>>().ToTable("User");
            modelBuilder.Entity<IdentityUserClaim<Guid>>().ToTable("UserClaim");
            modelBuilder.Entity<IdentityUserLogin<Guid>>().ToTable("UserLogin");
            modelBuilder.Entity<IdentityUserToken<Guid>>().ToTable("UserToken");

            modelBuilder.Entity<IdentityRole<Guid>>().ToTable("Role");
            modelBuilder.Entity<IdentityRoleClaim<Guid>>().ToTable("RoleClaim");

            modelBuilder.Entity<IdentityUserRole<Guid>>().ToTable("UserRole");


            //shadow 特性 影子属性
            // modelBuilder.Entity<Blog>().Property<DateTime>("IsDeleted");

            var t = modelBuilder.Model.GetEntityTypes()
                .Where(e => typeof(ISoftDleteEntity).IsAssignableFrom(e.ClrType));

            foreach (var entityType in t)
            {
                modelBuilder.Entity(entityType.ClrType).Property<Boolean>("IsDeleted");
                var parameter = Expression.Parameter(entityType.ClrType, "e");
                var body = Expression.Equal(
                    Expression.Call(typeof(EF), nameof(EF.Property), new[] { typeof(bool) }, parameter, Expression.Constant("IsDeleted")),
                    Expression.Constant(false));

                modelBuilder.Entity(entityType.ClrType).HasQueryFilter(Expression.Lambda(body, parameter));
            }
            //modelBuilder.Entity<Post>().HasQueryFilter(e => e.TenantId == _tenantId && !e.IsDeleted);
            //ChangeTracker.SetShadowISoftDeletableProperties();


            //System.Reflection.Assembly mapAssembly = Assembly.GetExecutingAssembly();
            //modelBuilder.ApplyConfigurationsFromAssembly(mapAssembly);

            base.OnModelCreating(modelBuilder);
        }

        #region IDbContext

        public int ExecuteSqlCommand(string sql, params object[] parameters)
        {
            return this.Database.ExecuteSqlRaw(sql, parameters);
        }

        public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters)
        {
            throw new NotImplementedException();
        }
        public int Commit()
        {
            try
            {
                return base.SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex)
            {
                //数据库命令未影响预期行数。这通常指示存在乐观并发冲突；即，某行自对它查询以来已在数据库中发生更改。
                // Get the current entity values and the values in the database
                // as instances of the entity type
                var entry = ex.Entries.Single();
                var databaseValues = entry.GetDatabaseValues();
                // Update the original values with the database values and       
                entry.OriginalValues.SetValues(databaseValues);
                throw ex;
            }
            catch (DbUpdateException ex) //向数据库发送更新时出错。
            {
                throw ex;
            }
            catch (Exception)
            {
                // log.Debug(ex.ToString());
                throw;
            }
        }

        #endregion

        private void UpdateAuditEntities()
        {
            //var modifiedEntries = ChangeTracker.Entries()
            //    .Where(x => x.Entity is IAuditableEntity && (x.State == EntityState.Added || x.State == EntityState.Modified));


            //foreach (var entry in modifiedEntries)
            //{
            //    var entity = (IAuditableEntity)entry.Entity;
            //    DateTime now = DateTime.UtcNow;

            //    if (entry.State == EntityState.Added)
            //    {
            //        entity.CreatedDate = now;
            //        entity.CreatedBy = CurrentUserId;
            //    }
            //    else
            //    {
            //        base.Entry(entity).Property(x => x.CreatedBy).IsModified = false;
            //        base.Entry(entity).Property(x => x.CreatedDate).IsModified = false;
            //    }

            //    entity.UpdatedDate = now;
            //    entity.UpdatedBy = CurrentUserId;
            //}
        }
    }
}
